// Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.

// API出口过滤
package export

import (
	"fleetmanager/api/common/log"
	eModel "fleetmanager/api/model/event"
	eService "fleetmanager/api/service/event"
	"fleetmanager/logger"
	"fmt"

	// "io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
)

const (
	BadRequestCode    = 400
	NormalRequestCode = 200
)

const (
	NotRegisterEventReourceTypes = "session"
)

// Filter: API出口过滤器
func Filter(ctx *context.Context) {
	fields := make(map[string]interface{}, 0)

	endTime := time.Now()
	var startUnix int64 = 0
	var endUnix = endTime.Unix()
	var durationMs int64 = 0
	t := ctx.Input.GetData(logger.StartTime)
	if startTime, ok := t.(time.Time); ok {
		durationMs = endTime.Sub(startTime).Milliseconds()
		startUnix = startTime.Unix()
	}

	status := 0
	code := ctx.Output.Status
	if code == 0 {
		code = ctx.Output.Context.ResponseWriter.Status
	}
	if code < BadRequestCode && code >= NormalRequestCode {
		status = 1
	}

	routerPattern := ""
	rp := ctx.Input.GetData("RouterPattern")
	if rp != nil {
		s, ok := rp.(string)
		if ok {
			routerPattern = s
		}
	}
	fields[logger.RequestId] = ctx.Input.GetData(logger.RequestId)
	fields[logger.RequestRawUri] = ctx.Input.URL()
	fields[logger.ClientIp] = ctx.Input.Context.Request.RemoteAddr
	fields[logger.ResourceName] = routerPattern
	fields[logger.RequestMethod] = ctx.Input.Method()
	fields[logger.RequestQuery] = ctx.Input.Context.Request.URL.RawQuery
	fields[logger.RequestBody] = string(ctx.Input.RequestBody)
	fields[logger.ResponseStatus] = status
	fields[logger.ResponseCode] = code
	fields[logger.ServiceName] = "fleet_manager"
	fields[logger.StartTime] = startUnix
	fields[logger.EndTime] = endUnix
	fields[logger.DurationMs] = durationMs
	logger.A.WithFields(fields).Info("api event")
	checkEvent(ctx)
}

func checkEvent(ctx *context.Context) {
	isEvent, traceState := checkIsEvent(ctx)
	if !isEvent {
		return
	}

	eventName := ctx.Input.Method()
	routerInfo, isFind := web.BeeApp.Handlers.FindRouter(ctx)
	routeUrls := strings.Split(routerInfo.GetPattern(), "/")
	if len(routeUrls) > 3 {
		// 服务端会话与客户端会话特判，该类型事件由appgateway上报
		if strings.Contains(routeUrls[3], NotRegisterEventReourceTypes) &&
			traceState == eService.EventTraceStateNormal {
			return
		}
		eventName += "-" + routeUrls[3]
	}
	if isFind {
		eventName += "-" + routerInfo.GetMethod()[ctx.Input.Method()]
	}
	smnMessage := fmt.Sprintf(eService.EventSmnMessagePattern, eventName,
		eService.EventResourceTypeRequest, eService.EventResourceTraceService, traceState)
	remoteAddr := strings.Split(ctx.Input.Context.Request.RemoteAddr, ":")
	addr := ""
	if len(remoteAddr) > 0 {
		addr = remoteAddr[0]
	} else {
		// 上报全0 ip地址，便于定位
		addr = "0.0.0.0"
	}
	eventReq := &eModel.RegisterEventRequest{
		Name:         eventName,
		ResourceType: eService.EventResourceTypeRequest,
		TraceService: eService.EventResourceTraceService,
		TraceState:   traceState,
		EventType:    eService.EventTypeData,
		SourceIp:     addr,
		Details: "request-body:" + string(ctx.Input.RequestBody) + "; " +
			"request-query:" + ctx.Input.Context.Request.URL.RawQuery + "; " +
			"request-url:" + ctx.Input.URL() + ";",
		SmnMessage: smnMessage,
	}
	ctx.Output.IsEmpty()
	tLogger := log.GetTraceLogger(ctx).WithField(logger.Stage, "event")
	es := eService.NewEventService(ctx.Input.Param(":project_id"), tLogger)
	go es.RegisterEvent(eventReq)
}

func checkIsEvent(ctx *context.Context) (bool, string) {
	traceState := getTraceState(ctx)
	if traceState != eService.EventTraceStateNormal {
		return true, traceState
	}
	// get请求直接跳过
	if ctx.Input.Method() != http.MethodPost &&
		ctx.Input.Method() != http.MethodPut &&
		ctx.Input.Method() != http.MethodDelete {
		return false, ""
	}
	// 项目ID为空跳过
	if ctx.Input.Param(":project_id") == "" {
		return false, ""
	}
	return true, traceState
}

func getTraceState(ctx *context.Context) string {
	traceState := eService.EventTraceStateNormal
	code := ctx.Output.Status
	if code == 0 {
		code = ctx.Output.Context.ResponseWriter.Status
	}
	if code == BadRequestCode {
		traceState = eService.EventTraceStateWarning
	}
	if code > BadRequestCode {
		traceState = eService.EventTraceStateIncident
	}
	return traceState
}
